Skip to content

feat:add streaming API interceptor#549

Open
AstaTus wants to merge 2 commits intojackwener:mainfrom
AstaTus:main
Open

feat:add streaming API interceptor#549
AstaTus wants to merge 2 commits intojackwener:mainfrom
AstaTus:main

Conversation

@AstaTus
Copy link
Copy Markdown

@AstaTus AstaTus commented Mar 28, 2026

Description

feat: add streaming API interceptor for background tab compatibility
Adds streaming-capable network interception that works in background
browser tabs where requestAnimationFrame is throttled. This enables
capturing streaming API responses (e.g., Gemini's StreamGenerate XHR)
without relying on DOM rendering.

Key changes:

  • New generateStreamingInterceptorJs() with fetch ReadableStream + XHR
    onprogress/readystatechange dual capture
  • New waitForStreamCaptureJs() with configurable timeout and done-waiting
  • sendCommand() now accepts optional timeoutMs (default 30s)
  • IPage extended with installStreamingInterceptor/getStreamedResponses/
    waitForStreamCapture methods
  • New stream-intercept pipeline step for YAML pipelines
  • XHR completion uses full responseText overwrite via readystatechange
    to guarantee data completeness

Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com

Related issue:

Type of Change

  • 🐛 Bug fix
  • ✨ New feature
  • 🌐 New site adapter
  • 📝 Documentation
  • ♻️ Refactor
  • 🔧 CI / build / tooling

Checklist

  • I ran the checks relevant to this PR
  • I updated tests or docs if needed
  • I included output or screenshots when useful

Documentation (if adding/modifying an adapter)

  • Added doc page under docs/adapters/ (if new adapter)
  • Updated docs/adapters/index.md table (if new adapter)
  • Updated sidebar in docs/.vitepress/config.mts (if new adapter)
  • Updated README.md / README.zh-CN.md when command discoverability changed
  • Used positional args for the command's primary subject unless a named flag is clearly better
  • Normalized expected adapter failures to CliError subclasses instead of raw Error

Screenshots / Output

@AstaTus AstaTus changed the title add gemini-web plugin link feat(doc)add gemini-web plugin link Mar 28, 2026
@AstaTus AstaTus changed the title feat(doc)add gemini-web plugin link feat:add streaming API interceptor for background tab compatibility and add plugin link Mar 28, 2026
@AstaTus
Copy link
Copy Markdown
Author

AstaTus commented Mar 28, 2026

after this pr merge , I will update opencli-plugin-gemini-web ,the new opencli-plugin-gemini-web uses streaming API interceptor

Copy link
Copy Markdown
Contributor

@Astro-Han Astro-Han left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The streaming interceptor idea is solid — background-tab rAF throttling is a real pain point for AI chat platforms. A few things to address:

Structural

  1. The plugin link addition and the streaming interceptor are unrelated changes — ideally separate PRs.
  2. package.json version bump (1.5.5 → 1.5.6) should be left to the maintainer.

Testing

  1. No tests for the new code (stream-intercept.ts, generateStreamingInterceptorJs, waitForStreamCaptureJs, generateReadStreamJs). The existing test changes only add empty mocks to satisfy the expanded IPage interface.

Functional

  1. The fetch interceptor resets state (window.${prefix}_text = '') when a new matching request arrives. If two matching requests overlap, the second silently wipes the first's data.
  2. generateReadStreamJs clears all state on read — calling it twice loses data, and there's no option to peek without clearing.

Minor

  1. English README plugin description uses Chinese (Web端Gemini交互) — should be English to match the other rows.
  2. Plugin type column shows while others use YAML or TS.

@Astro-Han
Copy link
Copy Markdown
Contributor

Astro-Han commented Apr 2, 2026

Follow-up to my earlier review: there is also some state leakage around the XHR SSE buffer.

In generateStreamingInterceptorJs(), the XHR path accumulates partial SSE chunks in window.__opencli_stream_sse_buf during progress, and only clears that buffer on the readystatechange/load completion path.

But generateReadStreamJs() does not clear ${prefix}_sse_buf, and a new matching request also does not reset it before starting a fresh capture.

That means if a caller reads early (waitForDone: false) or the previous XHR stream stops in a non-clean path, the next matching request can inherit leftover partial SSE data from the previous one and parse mixed events.

I think ${prefix}_sse_buf should be treated as part of the capture state and reset both:

  • when a new matching request starts, and
  • when generateReadStreamJs() clears the rest of the streaming state.

There is also a compatibility bug in the SSE parser itself: both the fetch path and XHR path only look for \n\n as the event boundary (src/interceptor.ts, around the indexOf('\\n\\n') checks).

That works for LF-only streams, but a compliant SSE server can also use CRLF line endings. With a stream like data: first\r\n\r\ndata: second\r\n\r\n, this code never finds a boundary during accumulation, then flushes the whole buffer at the end and parses it as one merged event instead of two.

So for some perfectly valid SSE responses, returnEvents: true will return the wrong event structure even when the raw stream content is fine. I think the delimiter handling needs to normalize \r\n first, or split on both LF and CRLF boundaries.

@AstaTus
Copy link
Copy Markdown
Author

AstaTus commented Apr 2, 2026

  1. add unit tests for generateStreamingInterceptorJs generateReadStreamJs, waitForStreamCaptureJs
  2. single-capture pattern, state reset on new request is expected
  3. generateReadStreamJs added clear parameter (default true); IPage.getStreamedResponses accepts { clear?: boolean }
  4. _sse_buf cleared in 4 places: install init, fetch new request, XHR new request, generateReadStreamJs
  5. SSE parser only splits on \n\n, breaks on CRLF (\r\n\r\n): \r\n → \n normalization in 3 places: fetch SSE buffer, XHR SSE buffer, __parseSse line split

@AstaTus AstaTus requested a review from Astro-Han April 2, 2026 09:36
Copy link
Copy Markdown
Contributor

@Astro-Han Astro-Han left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updates — the tests and peek mode (clear parameter) look solid. Most of my earlier concerns are addressed.

Two small things remaining:

  1. PR title still mentions "add plugin link" but those changes are no longer in the diff. Might want to update the title to just the streaming interceptor part.

  2. The new stream-intercept pipeline step has a nice YAML usage example in the code comment, but no corresponding doc page under docs/. Worth adding if this is intended for adapter authors to use.

Otherwise this looks good to me.

@AstaTus AstaTus changed the title feat:add streaming API interceptor for background tab compatibility and add plugin link feat:add streaming API interceptor Apr 2, 2026
@AstaTus
Copy link
Copy Markdown
Author

AstaTus commented Apr 2, 2026

  1. change PR titile
  2. update ts-adapter.md and yaml-adapter.md‎ about stream API interceptor

@AstaTus AstaTus requested a review from Astro-Han April 2, 2026 10:09
Copy link
Copy Markdown
Contributor

@Astro-Han Astro-Han left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all the changes, LGTM.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants